all files / src/ game.ts

100% Statements 111/111
100% Branches 22/22
100% Functions 57/57
100% Lines 109/109
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250       13× 11× 11× 11×   18×     23× 23× 18×     17×   18×     23×     11×       79× 11×     11× 11× 176×     176× 176×   176×     176×                                   84×   12×                                                                                                                     12× 12×                                                                               176× 176× 176×     11×  
import { createStore } from 'redux';
import * as _ from 'lodash';
 
import { Game, Card, Suit, Rank, Player, Phase, Thousand, CardPattern } from './game.interfaces';
import { registerPlayer, setDeck, dealCardToPlayer, dealCardToStock, bid, setPhase, ASSIGN_STOCK, assignStock, shareStock, initializeBattle, calculateBattleResult, increaseBid } from './game.actions';
import { game as gameReducer } from './game.reducer';
import { createDeck, getMarriages, createShuffledDeck } from './helpers/cards.helpers';
import { isRegisteringPlayersPhaseFinished } from './validators/player.validator';
import { isBattleFinished, isTrickFinished } from './validators/battle.validator';
import { getNextTurn, getWinner, getNextBiddingTurn } from './helpers/players.helpers';
import { isBiddingFinished } from './validators/bid.validator';
import { isSharingStockFinished } from './validators/stock.validator';
import { can, isGameFinished } from './validators/game.validators';
import { getBidWinner, noOneParticipatedInBidding } from './helpers/bid.helpers';
import { throwCard, initializeBidding, finalizeTrick, declareBomb } from './game.actions';
import { getTrickWinner } from './helpers/battle.helpers';
var EventEmitter = require('wolfy87-eventemitter');
 
export function initializeGame(defaultState: Game = undefined): Thousand {
    const store = createStore(gameReducer, defaultState);
    const events: any = new EventEmitter();
    let emitActionEvent = null;
 
    function someActionListenersRegistered() {
        return events.getListeners('action').length > 0;
    }
 
    function manageAction(action: any) {
        let result = false;
        if (can(store.getState(), action)) {
            if(someActionListenersRegistered()) {
                events.emit('action', action, () => {
                    store.dispatch(action);
                });
            } else {
                store.dispatch(action);
            }
            result = true;
        } else {
            result = false;
        }
        return result;
    }   
 
    const thousand: Thousand = {
        events,
        //actions:
        registerPlayer: player =>                                                manageAction(registerPlayer(player)),
        bid: (player: string, value: number) =>                                  manageAction(bid(player, value)),
        pass: (player: string) =>                                                manageAction(bid(player, 0)),
        shareStock: (player: string, card: CardPattern, targetPlayer: string) => manageAction(shareStock(player, card, targetPlayer)),
        throwCard: (card: CardPattern, player: string) =>                        manageAction(throwCard(card, player)),
        declareBomb: (player: string) =>                                         manageAction(declareBomb(player)),
        increaseBid: (player: string, value: number) =>                          manageAction(increaseBid(player, value)),
        //utils:
        getState: () => store.getState(),
        init: () => store.dispatch(setPhase(store.getState().phase))
    };
    
    let previousPhase: Phase = null;
    store.subscribe(() => {
        onStoreChanged();
    });
 
    function onStoreChanged() {
        const state: Game = store.getState();
        const isNew = (previousPhase !== state.phase);
 
        function updatePreviousState() {
            previousPhase = state.phase;
        }
        
        const phaseHandler = {
            [Phase.REGISTERING_PLAYERS_START]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => store.dispatch(setPhase(Phase.REGISTERING_PLAYERS_IN_PROGRESS)),
                    isFirst
                );
            },
            [Phase.REGISTERING_PLAYERS_IN_PROGRESS]: (isFirst) => {
                events.emit('phaseUpdated', () => {}, isFirst);
                if (isRegisteringPlayersPhaseFinished(state)) {
                    store.dispatch(setPhase(Phase.REGISTERING_PLAYERS_FINISHED));
                }
            },
            [Phase.REGISTERING_PLAYERS_FINISHED]: (isFirst) => {
                events.emit(
                    'phaseUpdated', 
                    () => store.dispatch(setPhase(Phase.DEALING_CARDS_START)),
                    isFirst
                );
            },
            [Phase.DEALING_CARDS_START]: (isFirst) => {
                events.emit(
                    'phaseUpdated', 
                    () => {
                        store.dispatch(setPhase(Phase.DEALING_CARDS_IN_PROGRESS));
                        
                        store.dispatch(setDeck(createShuffledDeck()));
                        for (let i = 0; i < 7; i++) {
                            _.each(state.players, (player: Player) => store.dispatch(dealCardToPlayer(player.id)))
                        }
                        for (let i = 0; i < 3; i++) {
                            store.dispatch(dealCardToStock());
                        }
                        store.dispatch(setPhase(Phase.DEALING_CARDS_FINISHED));
                    },
                    isFirst
                );
            },
            [Phase.DEALING_CARDS_FINISHED]: (isFirst) => {
                events.emit(
                    'phaseUpdated', 
                    () => store.dispatch(setPhase(Phase.BIDDING_START)),
                    isFirst
                );
            },
            [Phase.BIDDING_START]: (isFirst) => {
                events.emit(
                    'phaseUpdated', 
                    () => {                        
                        store.dispatch(initializeBidding());
                    },
                    isFirst
                );
            },
            [Phase.BIDDING_IN_PROGRESS]: (isFirst) => {
                events.emit('phaseUpdated', () => {}, isFirst);
                if (isBiddingFinished(state)) {
                    store.dispatch(setPhase(Phase.BIDDING_FINISHED));
                }
            },
            [Phase.BIDDING_FINISHED]: (isFirst) => {
                events.emit(
                    'phaseUpdated', 
                    () => {
                        if(noOneParticipatedInBidding(state.bid)) {
                            store.dispatch(setPhase(Phase.ASSIGN_STOCK));
                        } else {
                            store.dispatch(setPhase(Phase.FLIP_STOCK));
                        }
                    },
                    isFirst
                );
            },               
            [Phase.FLIP_STOCK]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => store.dispatch(setPhase(Phase.ASSIGN_STOCK)),
                    isFirst
                );
            },
            [Phase.ASSIGN_STOCK]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => {
                        store.dispatch(assignStock());
                    },
                    isFirst
                );
            },
            [Phase.SHARE_STOCK]: (isFirst) => {
                events.emit('phaseUpdated', () => {}, isFirst);
                if (isSharingStockFinished(state)) {
                    store.dispatch(initializeBattle());
                }
            },
            [Phase.BOMB_DECLARED]: (isFirst) => {
                events.emit('phaseUpdated', () => {
                    store.dispatch(setPhase(Phase.DEALING_CARDS_START));
                }, isFirst);
            },
            [Phase.BATTLE_START]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => store.dispatch(setPhase(Phase.TRICK_START)),
                    isFirst
                );
            },
            [Phase.TRICK_START]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => store.dispatch(setPhase(Phase.TRICK_IN_PROGRESS)),
                    isFirst
                );
            },            
            [Phase.TRICK_IN_PROGRESS]: (isFirst) => {
                events.emit('phaseUpdated', () => {}, isFirst);
                if(isTrickFinished(state)) {
                    store.dispatch(setPhase(Phase.TRICK_FINISHED));
                }
            },
            [Phase.TRICK_FINISHED]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => {
                        store.dispatch(finalizeTrick(getTrickWinner(state)));
                    },
                    isFirst
                );
            },
            [Phase.ASSIGN_TRICK_CARDS]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => {
                        if(isBattleFinished(state)) {
                            store.dispatch(setPhase(Phase.BATTLE_FINISHED));
                        } else {
                            store.dispatch(setPhase(Phase.TRICK_START))
                        }
                    },
                    isFirst
                );
            },
             [Phase.BATTLE_FINISHED]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => store.dispatch(calculateBattleResult()),
                    isFirst
                );
            },
            [Phase.BATTLE_RESULTS_ANNOUNCEMENT]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => {
                        if (isGameFinished(state)) {
                            store.dispatch(setPhase(Phase.GAME_FINISHED));
                        } else {
                            store.dispatch(setPhase(Phase.DEALING_CARDS_START));
                        }
                    },
                    isFirst
                );
            },
            [Phase.GAME_FINISHED]: (isFirst) => {
                events.emit(
                    'phaseUpdated',
                    () => {},
                    isFirst
                );
            }
        };
        const isFirst = previousPhase !== state.phase;
        updatePreviousState();
        phaseHandler[state.phase] && phaseHandler[state.phase](isFirst);
    }
 
    return thousand;
}